package com.service.impl;

import com.dao.UploadDao;
import com.dto.UploadFileDto;
import com.entity.SysChunk;
import com.entity.SysFile;
import com.service.FileUploadService;
import com.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.List;

@Slf4j
@Component
public  class FileUploadServiceImpl implements FileUploadService {

   @Autowired
   UploadDao  uploadDao;

   String filePath;

   @Value("${upload.path}")
   String baseUploadPath;

    @Override
    public SysFile chunkFileUpload(UploadFileDto dto) throws IOException {

        //        获取文件路径
        filePath = Utils.createFilePath(baseUploadPath,dto.getCreateUserId());  //C:\Users\Administrator\Desktop\file\20230114

        MultipartFile multipartFile = dto.getFile();

        //        根据路径创建文件
        String chunkName = getChunkName(dto.getName(),dto.getChunkIndex());
        File file = new File(baseUploadPath + filePath, chunkName);
        SysChunk sysChunkEntity = getSysChunkEntity(dto,chunkName,filePath);
        //        保存文件到指定路径
        multipartFile.transferTo(file);
        //        保存分片信息到数据库
        uploadDao.saveChunk(sysChunkEntity);
        //        查询分片信息
        return  chunkInfo(dto);
    }

    @Override
    public SysFile singleFileUpload(UploadFileDto uploadFileDto) throws IOException {
        MultipartFile multipartFile = uploadFileDto.getFile();
        //        获取文件路径
        filePath = Utils.createFilePath(baseUploadPath,uploadFileDto.getCreateUserId());
        File file = new File(baseUploadPath + filePath, uploadFileDto.getName());
        multipartFile.transferTo(file);
        return saveFile(uploadFileDto);
    }


    private SysChunk getSysChunkEntity(UploadFileDto dto, String fileName, String path){

        SysChunk chunkRecord = new SysChunk();
        chunkRecord.setChunkFileName(fileName);
        chunkRecord.setChunkFilePath(path + fileName);
        chunkRecord.setChunks(dto.getChunks());
        chunkRecord.setChunkSize(dto.getChunkSize());
        chunkRecord.setChunkIndex(dto.getChunkIndex());
        chunkRecord.setFileMd5(dto.getFileMd5());
        chunkRecord.setCurrentChunkMd5(dto.getCurrentChunkMd5());
        chunkRecord.setCreateUserId(dto.getCreateUserId());
        return  chunkRecord;
    }

    private String getChunkName(String fileName,Integer chunkIndex){
        int lastIndexOf = fileName.lastIndexOf(".");
        String chunkName = fileName.substring(0,lastIndexOf) + chunkIndex + fileName.substring(lastIndexOf,fileName.length());
        return chunkName;
    }

    private SysFile saveFile(UploadFileDto dto){

        String fileName = dto.getName();
        int index = fileName.lastIndexOf(".");
        String subFixed = fileName.substring(index, fileName.length());

        SysFile sysFile = new SysFile();
        sysFile.setChunkFlag(dto.getChunkFlag());
        sysFile.setFileName(dto.getName());
        sysFile.setExtension(subFixed);
        sysFile.setFileSize(dto.getSize());
        sysFile.setFileType(dto.getType());
        sysFile.setFilePath(filePath + dto.getName());
        sysFile.setFileMd5(dto.getFileMd5());
        sysFile.setRemark("无备注信息");
        sysFile.setCreateUserId(dto.getCreateUserId());
        uploadDao.saveFile(sysFile);
        return sysFile;
    }

    //  synchronized添加线程锁
    private synchronized  SysFile   chunkInfo(UploadFileDto dto) throws IOException {
        HashMap<Object, Object> chunkListMap = new HashMap<>();
        chunkListMap.put("fileMd5",dto.getFileMd5());
        chunkListMap.put("createUserId",dto.getCreateUserId());
        List<SysChunk> chunkList = uploadDao.getChunkList(chunkListMap);
//        循环数据库分片信息
        if ( chunkList.size() == dto.getChunks()){
//            全部的分片上传完成后，开始合并分片
            mergeChunk(chunkList,dto);
//          保存完整文件信息到数据库
            return saveFile(dto);
        }
        return null;
    }

    private   void mergeChunk(List<SysChunk> chunkList,UploadFileDto dto) throws IOException {
        String fileUrl =baseUploadPath + filePath + dto.getName();
        RandomAccessFile randomAccessFile = new RandomAccessFile(fileUrl, "rw");//       参数填file或fileUrl都可以     操作文件的方式（“r”, "rw"）,常用的是rw：读操作和写操作都是允许的
        //      获取Channel，可以通过channel对数据进行读写
        FileChannel channel = randomAccessFile.getChannel();
        //      文件第一次创建时 randomAccessFile.length() == 0，需要设置新创建文件的存储大小
        if (randomAccessFile.length() == 0) {
            //            设置文件的总大小信息
            randomAccessFile.setLength(dto.getSize());
        }
        for (SysChunk chunk : chunkList) {
            //          根据当前分片的下标，计算插入数据的位置
            int position = chunk.getChunkIndex() * chunk.getChunkSize();
            //          插入文件的大小
            File file1 = new File(baseUploadPath + chunk.getChunkFilePath());
            MultipartFile cMultiFile = new MockMultipartFile("file", file1.getName(), null, new FileInputStream(file1));
            long fileSize = cMultiFile.getSize();
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, position, fileSize);
            //          开始合并文件数据
            buffer.put(cMultiFile.getBytes());
            //          清空buffer
            Utils.cleanBuffer(buffer);
            file1.delete();

            //  删除数据库的分片信息
            HashMap<Object, Object> chunkListMap = new HashMap<>();
            chunkListMap.put("currentChunkMd5",chunk.getCurrentChunkMd5());
            chunkListMap.put("createUserId",dto.getCreateUserId());
            uploadDao.deleteChunk(chunkListMap);
        }

        //          关闭数据对象
        channel.close();
        //          关闭此RandomAccessFile流并释放与此流链接的所有其他系统资源
        randomAccessFile.close();
    }

    public SysFile getSysFile(String md5,Integer userId){
        HashMap<Object, Object> fileMap = new HashMap<>();
        fileMap.put("fileMd5",md5);
        fileMap.put("createUserId",userId);
        return uploadDao.getSysFileByMd5(fileMap);
    }

}
